/*
Copyright (C) 2011 by the PSVN Research Group, University of Alberta
*/

#ifndef _ABST_DESC_HPP
#define _ABST_DESC_HPP

#include <vector>
#include <string>
#include "psvn_game_so.h"

/* Describes a level in the abstraction hierarchy.
   Each level must have a valid pointer to a compiled_game_so_t and an 
   abstraction file describing how to abstract a state of the current
   level to the next level. */
class AbstractionDescription
{
public:
    void* m_so_handle;
    const compiled_game_so_t* m_game;
    void* m_abstraction;

    explicit AbstractionDescription();

    /* Creates a description of the abstraction hierarchy.
       File should contain the shared object file and the 
       abstraction for the next level. For example:
           level0.so level1.abst
           level1.so level2.abst
           level2.so       
    */
    static std::vector<AbstractionDescription> 
    Load(const std::string& file);

    /* Creates description from passed in object.
       Abstraction is set to null, so use for the highest level. */
    void Load(const compiled_game_so_t* game);

    /* Creates description from passed object and abstraction file. */
    void Load(const compiled_game_so_t* game, const char* filename);

private:
    void LoadAbstraction(const char* filename);
    static void GetStateSpaceName(const char* in, char* out);
};

class AbstHierarchy
{
private:
    std::vector<AbstractionDescription> m_desc;

public:
    AbstHierarchy(const std::vector<AbstractionDescription>& desc)
        : m_desc(desc)
    { };

    size_t NumLevels() const 
    { return m_desc.size(); }

    int StateSize() const 
    { return m_desc[0].m_game->state_size; }

    bool Equal(const void* s1, const void* s2) const
    { return compare_so_states(s1, s2, m_desc[0].m_game) == 0; }
    
    int64_t Hash(const void* state) const
    { return hash_so_state(state, m_desc[0].m_game); }

    void Copy(void* dest, const void* from) const
    { copy_so_state(dest, from, m_desc[0].m_game); }

    void PrintState(FILE *stream, const void* state) const
    { print_so_state(stream, state, m_desc[0].m_game); }

    bool IsGoal(const int i, const void* state) const
    { return is_so_goal(state, m_desc[i].m_game); }

    void CopyGoalState(const int i, void* state, int n) const
    {  m_desc[i].m_game->init_goal_state(state, n); }
    
    bool NextGoalState(const int i, void* state, int* n) const
    {  return m_desc[i].m_game->next_goal_state(state, n); }

    int CostOfCheapestFwdRule(const int i) const
    { return m_desc[i].m_game->cost_of_cheapest_fwd_rule; }

    int CostOfCheapestBwdRule(const int i) const
    { return m_desc[i].m_game->cost_of_cheapest_bwd_rule; }

    void AbstractToNext(const int i, const void* in, void* out) const
    {
        so_abstract_state(m_desc[i].m_abstraction, in, out, m_desc[i].m_game);
    }

    int Epsilon(const int i, const void* state) const
    {
        if (IsGoal(i, state))
            return 0;
        return cost_of_cheapest_so_fwd_rule(m_desc[i].m_game);
    }

    class FwdChildGenerator
    {
    public:
        FwdChildGenerator(const AbstHierarchy& ah, int level, const void* state)
            : m_game(ah.m_desc[level].m_game), m_state(state)
        {
            init_so_fwd_iter(m_iter, m_game);
        }
        
        bool Generate(void *child)
        {
            if ((m_rule_used = next_so_fwd_iter(m_iter, m_state)) >= 0) {
                apply_so_fwd_rule(m_rule_used, m_state, child, m_game);
                return true;
            }
            return false;
        }
        
        int Cost() const 
        { return so_fwd_rule_cost(m_rule_used, m_game); }
        
        const char* RuleName() const 
        { return m_game->fwd_rule_names[m_rule_used]; }
        
    private:
        const compiled_game_so_t* m_game;
        const void* m_state;
        so_func_ptr m_iter;
        int m_rule_used;
    };

    class FwdReverseChildGenerator
    {
    public:
        FwdReverseChildGenerator(const AbstHierarchy& ah, int level, 
                                 const void* state)
            : m_game(ah.m_desc[level].m_game),
              m_state(state),
              m_num(0)
        {
            so_func_ptr iter;
            init_so_fwd_iter(iter, m_game);
            int rule_used;
            while ((rule_used = next_so_fwd_iter(iter, m_state)) >= 0) {
                m_rule[m_num++] = rule_used;
            }
            assert(m_num <= 128);
        }
        
        bool Generate(void *child)
        {
            if (m_num > 0) {
                m_last_rule = m_rule[--m_num];
                apply_so_fwd_rule(m_last_rule, m_state, child, m_game);
                return true;
            }
            return false;
        }
        
        int Cost() const { return so_fwd_rule_cost(m_last_rule, m_game); }
        
        const char* RuleName() const 
        { return m_game->fwd_rule_names[m_last_rule]; }
        
    private:
        const compiled_game_so_t* m_game;
        const void* m_state;
        int m_rule[128];
        int m_num;
        int m_last_rule;
    };

    class BwdChildGenerator
    {
    public:
        BwdChildGenerator(const AbstHierarchy& ah, int level, const void* state)
            : m_game(ah.m_desc[level].m_game), m_state(state)
        {
            init_so_bwd_iter(m_iter, m_game);
        }
        
        bool Generate(void *child)
        {
            if ((m_rule_used = next_so_bwd_iter(m_iter, m_state)) >= 0) {
                apply_so_bwd_rule(m_rule_used, m_state, child, m_game);
                return true;
            }
            return false;
        }
        
        int Cost() const 
        { return so_bwd_rule_cost(m_rule_used, m_game); }
        
        const char* RuleName() const 
        { return m_game->bwd_rule_names[m_rule_used]; }
        
    private:
        const compiled_game_so_t* m_game;
        const void* m_state;
        so_func_ptr m_iter;
        int m_rule_used;
    };

};

#endif // _ABST_DESC_HPP
